﻿var colormap = {};
var rectangleIntersectionScratch;
var cornerPositionsScratch;
var TileLonlatsGroundImageryProvider = function TileLonlatsGroundImageryProvider(grids) {
    this.grids = grids
    this._quadtree = undefined;
    this._tilingScheme = new Cesium.WebMercatorTilingScheme();
    this._errorEvent = new Cesium.Event();
    this._levelZeroMaximumError = Cesium.QuadtreeTileProvider.computeDefaultLevelZeroMaximumGeometricError(
        this._tilingScheme
    );
    this.cartographicLimitRectangle = Cesium.Rectangle.clone(
        Cesium.Rectangle.MAX_VALUE
    );
    rectangleIntersectionScratch = new Cesium.Rectangle();
    cornerPositionsScratch = [
        new Cesium.Cartesian3(),
        new Cesium.Cartesian3(),
        new Cesium.Cartesian3(),
        new Cesium.Cartesian3(),
    ];
};
Object.defineProperties(TileLonlatsGroundImageryProvider.prototype, {
    quadtree: {
        get: function () {
            return this._quadtree;
        },
        set: function (value) {
            this._quadtree = value;
        },
    },
    ready: {
        get: function () {
            return true;
        },
    },
    tilingScheme: {
        get: function () {
            return this._tilingScheme;
        },
    },
    errorEvent: {
        get: function () {
            return this._errorEvent;
        },
    },
});

TileLonlatsGroundImageryProvider.prototype.update = function (frameState) {
    this._quadtree.beginFrame(frameState);
};
TileLonlatsGroundImageryProvider.prototype.initialize = function (frameState) {
    this._quadtree.render(frameState);
};
TileLonlatsGroundImageryProvider.prototype.beginUpdate = function (frameState) {};
TileLonlatsGroundImageryProvider.prototype.endUpdate = function (frameState) {
    this._quadtree.endFrame(frameState);
    for (var i = 0; i < this._quadtree._tilesToRender.length; i++) {
        if (this._quadtree._tilesToRender[i].data.geometryPrimitive)
            this._quadtree._tilesToRender[i].data.geometryPrimitive.update(
                frameState
            );
    }
};

TileLonlatsGroundImageryProvider.prototype.getLevelMaximumGeometricError = function (level) {
    return this._levelZeroMaximumError / (1 << level);
};

TileLonlatsGroundImageryProvider.prototype.loadTile = function (frameState, tile) {
    var m_tile = function m_tile(img) {
        this._tile = tile;
        this._img = img;
        m_tile.prototype.draw = function draw() {
            var tile = this._tile;
            var img = this._img;
            var rectangle = new Cesium.Rectangle();
            //new Cesium.WebMercatorTilingScheme({numberOfLevelZeroTilesX:2,numberOfLevelZeroTilesY:1}).tileXYToRectangle(tile.x,tile.y,tile.level,rectangle);
            //GeographicTilingScheme
            new Cesium.WebMercatorTilingScheme().tileXYToRectangle(
                tile.x,
                tile.y,
                tile.level,
                rectangle
            );
            tile.data.geometryPrimitive = new Cesium.GroundPrimitive({
                geometryInstances: new Cesium.GeometryInstance({
                    geometry: new Cesium.RectangleGeometry({
                        rectangle: rectangle,
                        //vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                    }),
                }),
                appearance: new Cesium.EllipsoidSurfaceAppearance({
                    aboveGround: false,
                    material: new Cesium.Material({
                        fabric: {
                            type: "Image",
                            uniforms: {
                                image: img,
                            },
                        },
                    }),
                }),
            });
            tile.state = Cesium.QuadtreeTileLoadState.DONE;
            tile.renderable = true;
        };
    };

    if (tile.state === Cesium.QuadtreeTileLoadState.START) {
        tile.state = Cesium.QuadtreeTileLoadState.LOADING;
        tile.data = {
            geometryPrimitive: undefined,
            freeResources: function () {
                if (this.geometryPrimitive && this.geometryPrimitive.destory) {
                    this.geometryPrimitive.destory();
                    this.geometryPrimitive = undefined;
                }
            },
        };

        if (tile._level < 0) {
            tile.state = Cesium.QuadtreeTileLoadState.DONE;
            tile.renderable = true;
            return;
        }

        //var resource = new Cesium.Resource({
        //    url: 'http://10.32.24.168:8580/geoserver/gwc/service/wmts',
        //    queryParameters: {
        //        FORMAT: 'image/png',
        //        STYLE: '',
        //        REQUEST: 'GetTile',
        //        SERVICE: 'WMTS',
        //        VERSION: '1.0.0',
        //        LAYER: 'VEC:hb_color',
        //        TILEMATRIX: 'EPSG:4326:' + tile._level,
        //        TILEMATRIXSET: 'EPSG:4326',
        //        TILECOL: tile._x,
        //        TILEROW: tile._y
        //    }
        //});
        // var resource = new Cesium.Resource({
        //     url:
        //         "http://127.0.0.1:5503/Build/CesiumUnminified/Widgets/Images/ImageryProviders/naturalEarthII.png",
        // });

        //resource.fetchImage().then(function(results) {
        //    var tile = new m_tile(results);
        //    tile.draw();
        //    //if(tile._json.features.length>0)
        //    //    tile.draw();
        //});

        var canvas = document.createElement("canvas");
        canvas.width = 256;
        canvas.height = 256;
        var context = canvas.getContext("2d");
        var cssColor// = tile.level == 18 ? "#00ffff" : "#ffffff";
        if (this.grids && this.grids.indexOf("x:" + tile.x + ",y:" + tile.y + ",level:" + tile.level) >-1 ) {
            cssColor = "#ff0000"
        }
        //  else if (tile.level == 18) {
        //     cssColor = "#00ffff"
        // } 
        else {
            cssColor = "#ffff00";
        }
        context.strokeStyle = cssColor;
        context.lineWidth = 2;
        context.strokeRect(1, 1, 255, 255);
        context.font = "bold 25px Arial";
        context.textAlign = "center";
        context.fillStyle = cssColor;
        context.fillText("等级: " + tile.level, 124, 86);
        context.fillText("列: " + tile.x, 124, 136);
        context.fillText("行: " + tile.y, 124, 186);

        var img = canvas;
        var tile = new m_tile(img);
        tile.draw();
        var levelError = this.getLevelMaximumGeometricError(tile.level);
    }
};

TileLonlatsGroundImageryProvider.prototype.computeTileVisibility = function (
    tile,
    frameState,
    occluders
) {
    var distance = this.computeDistanceToTile(tile, frameState);
    tile._distance = distance;

    if (frameState.fog.enabled) {
        if (Cesium.Math.fog(distance, frameState.fog.density) >= 1.0) {
            // Tile is completely in fog so return that it is not visible.
            return Cesium.Visibility.NONE;
        }
    }

    var surfaceTile = tile.data;
    var tileBoundingRegion = surfaceTile.tileBoundingRegion;

    if (surfaceTile.boundingVolumeSourceTile === undefined) {
        // We have no idea where this tile is, so let's just call it partially visible.
        return Cesium.Visibility.PARTIAL;
    }

    var cullingVolume = frameState.cullingVolume;
    var boundingVolume = surfaceTile.orientedBoundingBox;

    if (!boundingVolume && surfaceTile.renderedMesh) {
        boundingVolume = surfaceTile.renderedMesh.boundingSphere3D;
    }

    // Check if the tile is outside the limit area in cartographic space
    surfaceTile.clippedByBoundaries = false;
    var clippedCartographicLimitRectangle = clipRectangleAntimeridian(
        tile.rectangle,
        this.cartographicLimitRectangle
    );
    var areaLimitIntersection = Cesium.Rectangle.simpleIntersection(
        clippedCartographicLimitRectangle,
        tile.rectangle,
        rectangleIntersectionScratch
    );
    if (!areaLimitIntersection) {
        return Visibility.NONE;
    }
    if (!Cesium.Rectangle.equals(areaLimitIntersection, tile.rectangle)) {
        surfaceTile.clippedByBoundaries = true;
    }

    if (frameState.mode !== Cesium.SceneMode.SCENE3D) {
        boundingVolume = boundingSphereScratch;
        BoundingSphere.fromRectangleWithHeights2D(
            tile.rectangle,
            frameState.mapProjection,
            tileBoundingRegion.minimumHeight,
            tileBoundingRegion.maximumHeight,
            boundingVolume
        );
        Cartesian3.fromElements(
            boundingVolume.center.z,
            boundingVolume.center.x,
            boundingVolume.center.y,
            boundingVolume.center
        );

        if (
            frameState.mode === SceneMode.MORPHING &&
            surfaceTile.renderedMesh
        ) {
            boundingVolume = BoundingSphere.union(
                surfaceTile.renderedMesh.boundingSphere3D,
                boundingVolume,
                boundingVolume
            );
        }
    }

    if (!boundingVolume) {
        return Cesium.Intersect.INTERSECTING;
    }

    var clippingPlanes = this._clippingPlanes;
    if (clippingPlanes && clippingPlanes.enabled) {
        var planeIntersection = clippingPlanes.computeIntersectionWithBoundingVolume(
            boundingVolume
        );
        tile.isClipped = planeIntersection !== Intersect.INSIDE;
        if (planeIntersection === Cesium.Intersect.OUTSIDE) {
            return Cesium.Visibility.NONE;
        }
    }

    var intersection = cullingVolume.computeVisibility(boundingVolume);
    if (intersection === Cesium.Intersect.OUTSIDE) {
        return Cesium.Visibility.NONE;
    }

    var ortho3D =
        frameState.mode === Cesium.SceneMode.SCENE3D &&
        frameState.camera.frustum instanceof Cesium.OrthographicFrustum;
    if (frameState.mode === Cesium.SceneMode.SCENE3D && !ortho3D && occluders) {
        var occludeePointInScaledSpace = surfaceTile.occludeePointInScaledSpace;
        if (!occludeePointInScaledSpace) {
            return intersection;
        }

        if (
            occluders.ellipsoid.isScaledSpacePointVisible(
                occludeePointInScaledSpace
            )
        ) {
            return intersection;
        }

        return Cesium.Visibility.NONE;
    }

    return intersection;
};
TileLonlatsGroundImageryProvider.prototype.canRefine = function (tile, frameState, occluders) {
    if (tile.level < 23) return true;
    return false;
};
TileLonlatsGroundImageryProvider.prototype.showTileThisFrame = function (
    tile,
    context,
    frameState,
    commandList
) {
    if (tile.data.geometryPrimitive) {
        tile.data.geometryPrimitive.update(context, frameState, commandList);
    }
};
TileLonlatsGroundImageryProvider.prototype.computeDistanceToTile = function (tile, frameState) {
    // The distance should be:
    // 1. the actual distance to the tight-fitting bounding volume, or
    // 2. a distance that is equal to or greater than the actual distance to the tight-fitting bounding volume.
    //
    // When we don't know the min/max heights for a tile, but we do know the min/max of an ancestor tile, we can
    // build a tight-fitting bounding volume horizontally, but not vertically. The min/max heights from the
    // ancestor will likely form a volume that is much bigger than it needs to be. This means that the volume may
    // be deemed to be much closer to the camera than it really is, causing us to select tiles that are too detailed.
    // Loading too-detailed tiles is super expensive, so we don't want to do that. We don't know where the child
    // tile really lies within the parent range of heights, but we _do_ know the child tile can't be any closer than
    // the ancestor height surface (min or max) that is _farthest away_ from the camera. So if we compute distance
    // based that conservative metric, we may end up loading tiles that are not detailed enough, but that's much
    // better (faster) than loading tiles that are too detailed.

    var heightSource = updateTileBoundingRegion(
        tile,
        this.terrainProvider,
        frameState
    );
    var surfaceTile = tile.data;
    var tileBoundingRegion = surfaceTile.tileBoundingRegion;

    if (heightSource === undefined) {
        // Can't find any min/max heights anywhere? Ok, let's just say the
        // tile is really far away so we'll load and render it rather than
        // refining.
        return 9999999999.0;
    } else if (surfaceTile.boundingVolumeSourceTile !== heightSource) {
        // Heights are from a new source tile, so update the bounding volume.
        surfaceTile.boundingVolumeSourceTile = heightSource;

        var rectangle = tile.rectangle;
        if (
            rectangle &&
            rectangle.width < Cesium.Math.PI_OVER_TWO + Cesium.Math.EPSILON5
        ) {
            surfaceTile.orientedBoundingBox = Cesium.OrientedBoundingBox.fromRectangle(
                tile.rectangle,
                tileBoundingRegion.minimumHeight,
                tileBoundingRegion.maximumHeight,
                tile.tilingScheme.ellipsoid,
                surfaceTile.orientedBoundingBox
            );

            surfaceTile.occludeePointInScaledSpace = computeOccludeePoint1(
                this,
                surfaceTile.orientedBoundingBox.center,
                tile.rectangle,
                tileBoundingRegion.maximumHeight,
                surfaceTile.occludeePointInScaledSpace
            );
        }
    }

    var min = tileBoundingRegion.minimumHeight;
    var max = tileBoundingRegion.maximumHeight;

    if (surfaceTile.boundingVolumeSourceTile !== tile) {
        var cameraHeight = frameState.camera.positionCartographic.height;
        var distanceToMin = Math.abs(cameraHeight - min);
        var distanceToMax = Math.abs(cameraHeight - max);
        if (distanceToMin > distanceToMax) {
            tileBoundingRegion.minimumHeight = min;
            tileBoundingRegion.maximumHeight = min;
        } else {
            tileBoundingRegion.minimumHeight = max;
            tileBoundingRegion.maximumHeight = max;
        }
    }

    var result = tileBoundingRegion.distanceToCamera(frameState);

    tileBoundingRegion.minimumHeight = min;
    tileBoundingRegion.maximumHeight = max;

    return result;
};

function updateTileBoundingRegion(tile, terrainProvider, frameState) {
    var surfaceTile = tile.data;
    if (surfaceTile === undefined) {
        surfaceTile = tile.data = new Cesium.GlobeSurfaceTile();
    }

    if (surfaceTile.tileBoundingRegion === undefined) {
        surfaceTile.tileBoundingRegion = new Cesium.TileBoundingRegion({
            computeBoundingVolumes: false,
            rectangle: tile.rectangle,
            ellipsoid: tile.tilingScheme.ellipsoid,
            minimumHeight: 0,
            maximumHeight: 0,
        });
    }
    return tile;
}

function clipRectangleAntimeridian(tileRectangle, cartographicLimitRectangle) {
    if (cartographicLimitRectangle.west < cartographicLimitRectangle.east) {
        return cartographicLimitRectangle;
    }
    var splitRectangle = Rectangle.clone(
        cartographicLimitRectangle,
        splitCartographicLimitRectangleScratch
    );
    var tileCenter = Rectangle.center(tileRectangle, rectangleCenterScratch);
    if (tileCenter.longitude > 0.0) {
        splitRectangle.east = CesiumMath.PI;
    } else {
        splitRectangle.west = -CesiumMath.PI;
    }
    return splitRectangle;
}

function computeOccludeePoint1(tileProvider, center, rectangle, height, result) {
    var ellipsoidalOccluder = tileProvider.quadtree._occluders.ellipsoid;
    var ellipsoid = ellipsoidalOccluder.ellipsoid;

    var cornerPositions = cornerPositionsScratch;
    Cesium.Cartesian3.fromRadians(
        rectangle.west,
        rectangle.south,
        height,
        ellipsoid,
        cornerPositions[0]
    );
    Cesium.Cartesian3.fromRadians(
        rectangle.east,
        rectangle.south,
        height,
        ellipsoid,
        cornerPositions[1]
    );
    Cesium.Cartesian3.fromRadians(
        rectangle.west,
        rectangle.north,
        height,
        ellipsoid,
        cornerPositions[2]
    );
    Cesium.Cartesian3.fromRadians(
        rectangle.east,
        rectangle.north,
        height,
        ellipsoid,
        cornerPositions[3]
    );

    return ellipsoidalOccluder.computeHorizonCullingPoint(
        center,
        cornerPositions,
        result
    );
}
TileLonlatsGroundImageryProvider.prototype.isDestroyed = function () {
    return false;
};
TileLonlatsGroundImageryProvider.prototype.destroy = function () {
    return Cesium.destroyObject(this);
};
TileLonlatsGroundImageryProvider.prototype.updateForPick = function (frameState) {
    // Add the tile pick commands from the tiles drawn last frame.
    var drawCommands = this._drawCommands;
    for (var i = 0, length = this._usedDrawCommands; i < length; ++i) {
        frameState.commandList.push(drawCommands[i]);
    }
};